package com.autumn.zero.authorization.application.services.impl;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import com.autumn.mybatis.criterion.EntityQuery;
import com.autumn.util.AutoMapUtils;
import com.autumn.util.DataRollbackException;
import com.autumn.util.ExceptionUtils;
import com.autumn.zero.application.services.AbstractZeroQueryAppService;
import com.autumn.zero.authorization.application.dto.PermissionDto;
import com.autumn.zero.authorization.application.dto.PermissionGrantedInput;
import com.autumn.zero.authorization.application.dto.PermissionResourcesModuleDto;
import com.autumn.zero.authorization.application.dto.roles.RoleDto;
import com.autumn.zero.authorization.application.dto.roles.RoleInput;
import com.autumn.zero.authorization.application.dto.roles.RoleOutput;
import com.autumn.zero.authorization.application.dto.roles.RolePermissionOutput;
import com.autumn.zero.authorization.application.dto.roles.RoleUserOutput;
import com.autumn.zero.authorization.application.services.IRoleAppService;
import com.autumn.zero.authorization.entitys.roles.Role;
import com.autumn.zero.authorization.entitys.roles.RolePermission;
import com.autumn.zero.authorization.entitys.users.querys.UserRoleQuery;
import com.autumn.zero.authorization.repositorys.roles.IRoleRepository;
import com.autumn.zero.authorization.repositorys.users.querys.IUserRoleQueryRepository;
import com.autumn.zero.authorization.services.IAuthorizationService;
import com.autumn.zero.authorization.services.IResourcesService;
import com.autumn.zero.authorization.values.ResourcesModulePermissionTreeValue;

/**
 * 角色应用服务
 * 
 * @author 杨昌国 2018-12-10 15:57:40
 */
public class RoleAppServiceImpl extends AbstractZeroQueryAppService<Long, Role, IRoleRepository, RoleDto>
		implements IRoleAppService {

	@Autowired
	private IRoleRepository repository;

	@Autowired
	private IUserRoleQueryRepository userRoleQueryRepository;

	@Autowired
	private IAuthorizationService authorizationService;

	@Autowired
	private IResourcesService resourcesService;

	@Override
	public String getModuleName() {
		return "角色管理";
	}

	/**
	 * 
	 */
	@Override
	protected void queryByOrder(EntityQuery<Role> query) {
		query.orderBy(Role.FILED_SORT_ID).orderBy(Role.FILED_ID);
	}

	private Set<String> searchMembers = new HashSet<>();

	@Override
	protected Set<String> getSearchMembers() {
		if (searchMembers.size() == 0) {
			searchMembers.add(Role.FILED_NAME);
		}
		return searchMembers;
	}

	@Override
	protected IRoleRepository getQueryRepository() {
		return this.repository;
	}

	private List<RoleUserOutput> queryUsers(long roleId) {
		EntityQuery<UserRoleQuery> query = new EntityQuery<>(UserRoleQuery.class);
		query.eq(UserRoleQuery.FILED_ROLE_ID, roleId).orderBy(UserRoleQuery.FILED_ID);
		List<UserRoleQuery> roles = query.selectByList(this.userRoleQueryRepository);
		return AutoMapUtils.mapList(roles, RoleUserOutput.class);
	}

	@Override
	@Transactional(rollbackFor = DataRollbackException.class)
	public RoleOutput add(RoleInput input) {
		ExceptionUtils.checkNotNull(input, "input");
		input.valid();
		Role entity = AutoMapUtils.map(input, Role.class);
		authorizationService.addRole(entity);
		RoleOutput result = AutoMapUtils.map(entity, RoleOutput.class);
		result.setUsers(this.queryUsers(entity.getId()));
		return result;
	}

	@Override
	@Transactional(rollbackFor = DataRollbackException.class)
	public RoleOutput update(RoleInput input) {
		ExceptionUtils.checkNotNull(input, "input");
		input.valid();
		Role entity = AutoMapUtils.map(input, Role.class);
		authorizationService.updateRole(entity);
		RoleOutput result = AutoMapUtils.map(entity, RoleOutput.class);
		result.setUsers(this.queryUsers(entity.getId()));
		return result;
	}

	@Override
	public RoleOutput queryById(Long id) {
		Role entity = this.repository.get(id);
		RoleOutput result = null;
		if (entity != null) {
			result = AutoMapUtils.map(entity, RoleOutput.class);
			result.setUsers(this.queryUsers(entity.getId()));
		}
		return result;
	}

	@Override
	@Transactional(rollbackFor = DataRollbackException.class)
	public void deleteById(Long id) {
		authorizationService.deleteRoleById(id);
	}

	@Override
	@Transactional(rollbackFor = DataRollbackException.class)
	public RolePermissionOutput authorize(PermissionGrantedInput input) {
		ExceptionUtils.checkNotNull(input, "input");
		input.valid();
		List<RolePermission> permissions = AutoMapUtils.mapList(input.getPermissions(), RolePermission.class,
				(s, t) -> {
					t.setName(s.getPermissionName());
				});
		Role role = authorizationService.authorizeByRole(input.getId(), input.getResourcesType(), permissions);
		RolePermissionOutput result = AutoMapUtils.map(role, RolePermissionOutput.class);
		result.setPermissions(AutoMapUtils.mapList(permissions, PermissionResourcesModuleDto.class, (s, t) -> {
			t.setPermissionName(s.getName());
		}));
		return result;
	}

	@Override
	public RolePermissionOutput authorizeByQuery(PermissionDto input) {
		Role role = authorizationService.getRole(input.getId(), false);
		if (role == null) {
			return null;
		}
		RolePermissionOutput result = AutoMapUtils.map(role, RolePermissionOutput.class);
		List<RolePermission> permissions = authorizationService.queryRolePermissions(input.getId());
		result.setPermissions(AutoMapUtils.mapList(permissions, PermissionResourcesModuleDto.class, (s, t) -> {
			t.setPermissionName(s.getName());
		}));
		return result;
	}

	@Override
	public List<ResourcesModulePermissionTreeValue> authorizeByModulePermissionTree(PermissionDto input) {
		List<RolePermission> permissions = authorizationService.queryRolePermissions(input.getId());
		return resourcesService.matchByPermissionTree(input.getResourcesType(), permissions);
	}

	@Override
	@Transactional(rollbackFor = DataRollbackException.class)
	public void authorizeByAllPermission(PermissionDto input) {
		ExceptionUtils.checkNotNull(input, "input");
		input.valid();
		authorizationService.authorizeByRoleAllPermission(input.getId(), input.getResourcesType());
	}
}
